---
title: Import Resolution
slug: /import-resolution

description: How imports in a given file are found by Pyrefly and their bindings are resolved, including files that are being type checked
---

# Import Resolution

This doc describes how imports in a given file are found within Pyrefly while performing
a type check or resolving IDE language support operations.

NOTE: see the [Configuration documentation](./configuration.mdx) for more info on
the config options referenced below.

## Relative Imports

If the import is relative (starting with one or more dots), the import is
resolved relative to the path of the file importing it. A single dot at the
beginning of the import (e.g. `.file.to.import`) represents the current
directory, and more dots (e.g. `..other.file`) will continue to walk upward.

## Absolute Imports

For absolute imports, Pyrefly searches for a match in each of the following groups. The
matching process is explained in the next paragraph.

1. Try to import from the search path. See the [search path section](#search-path) for more information.
2. Try to import from `typeshed`.
3. Try to import from the fallback search path. See the [fallback search path section](#fallback-search-path) for
more information on the contents of the search path.
4. Try to import from the site package path. See the
[site package path section](#site-package-path) for more information on the contents
of the site package path.
5. Return an import error.

When searching for a match in one of the above groups, Pyrefly performs the following process
over two passes, one looking for stub *packages*, and the other looking for source *packages*. See
[Stub Files vs Source Files](#stub-files-vs-source-files) for more information.

1. Attempt to match each part of the name to directories in the group, selecting the first
match that is found.
2. If the result is a `.pyi` file or regular package (directory with an
`__init__.py`/`__init__.pyi` file), return the result. Otherwise, keep searching and
attempt to find a `.pyi` file or regular package.

### Search Path

The search path ([see `search-path` in configuration docs](configuration.mdx#search-path))
consists of several entries representing project files.

1. Search path from CLI args.
2. Search path from config files.
3. If [`disable-search-path-heuristics`](configuration.mdx#disable-search-path-heuristics)
   is not set, Pyrefly appends an import root directory to the search path.

The import root is:
1. `src/` if there's a `src/` directory in the same directory as the config file.
2. The parent directory (`..`) if there's an `__init__.py` or `__init__.pyi` in the same
   directory as the config file.
3. Otherwise, the directory containing the config file.

### Fallback Search Path

The fallback search path is a heuristic automatically constructed by Pyrefly to attempt to
find project files when there's no config file marking the project root, and Pyrefly
is unable to determine from other heuristics where an import root might be.
It is only constructed when
[`disable-search-path-heuristics`](configuration.mdx#disable-search-path-heuristics)
is not set.

The fallback search path consists of each directory from the directory containing
a given file to the root of your filesystem. For example, if you have the following setup:

```
/
|- projects/
   |- project_a/
   |  |- b/
   |  |  |- c.py
   |  |- d.py
   |- project_e/
      |- f.py
```

`c.py`'s fallback search path would be `['/projects/project_a/b', '/projects/project_a',
'/projects', '/']`
- `d.py` could be importable with the paths `d`, `project_a.d`, or `projects.project_a.d`.
- `f.py` could be importable with the paths `project_e.f` or `projects.project_e.f`.

`e.py`'s fallback search path would be `['/projects/project_a', '/projects', '/']`
- `c.py` could be importable with the paths `b.c`, `project_a.b.c`, or `projects.project_a.b.c`
- `f.py` could be importable with the paths `project_e.f` or `projects.project_e.f`

`f.py`'s fallback search path would be `['/projects/project_e', '/projects', '/']`
- `c.py` could be importable with the paths `project_a.b.c` or `projects.project_a.b.c`
- `d.py` could be importable with the paths `project_a.d` or `projects.project_a.d`

### Site Package Path

The site package path
([see `site-package-path` in configuration docs](configuration.mdx#site-package-path))
consists of several entries representing third-party packages.

1. Site package path from a config file (if no CLI override is present) or CLI args.
2. A site package path queried from a Python interpreter, if one could be found.
   See [Environment Autoconfiguration](configuration.mdx#environment-autoconfiguration)
   for more information on finding interpreters.

## Stub Files vs Source Files

A
[stub file](https://typing.python.org/en/latest/spec/distributing.html#stub-files)
is any file that ends with a `.pyi` file suffix. They have many uses, including
adding typing to non-Python extension code, distributing typing information
separate from implementation, or overriding an implementation with more accurate
typing information.

A stub package is a second package corresponding to a regular package, with `-stubs`
appended to its name. A `-stubs` package should only include stub files (`.pyi`),
which override any `.py` or `.pyi` files in the non-stubs package. These are preferred
when available, since they contain the interfaces a library exposes to developers. An
example of this includes the popular library [`pandas`](https://github.com/pandas-dev/pandas),
and its stub package, [`pandas-stubs`](https://github.com/pandas-dev/pandas-stubs).

When importing from a non-stubs package, Pyrefly loads typing information from
imports by first searching for a relevant `-stubs` package, then by looking at
the non-stubs package's `.pyi` files, then falls back to a `.py` file. See
[Absolute Imports](#absolute-imports) for details on when non-stubs packages
are allowed to be used for types, and how you can override that behavior.

## Editable Installs

When using static analysis tools with an editable install, the editable install should be configured to use `.pth`
files that contain file paths (`/project/src/module`) rather than executable lines (started with `import`) that
install import hooks. See [setuptools doc](https://setuptools.pypa.io/en/latest/userguide/development_mode.html)
and [PEP 660](https://peps.python.org/pep-0660/) for more information.

Import hooks can provide an editable installation that offers a more accurate representation of the actual installation
environment. However, since resolving module locations through an import hook
**requires executing Python code at runtime**, they are incompatible with Pyrefly and other static analysis tools that
operate without code execution. Consequently, when an editable install is configured to use import hooks, Pyrefly
will be unable to automatically locate and analyze the corresponding source files, resulting in incomplete type checking and
code analysis.

Setuptools build system uses import hooks by default for editable installations. To ensure compatibility between
setuptools-based editable installs and Pyrefly, setuptools must be configured to use path-based `.pth` files instead.
This configuration should be performed through the build frontend (such as `pip`) by specifying the appropriate
options during installation or in the project's configuration files.

### uv with setuptools

When using [uv](https://docs.astral.sh/uv/) with setuptools, uv can be
[configured](https://docs.astral.sh/uv/reference/settings/#config-settings) to avoid import hooks.

NOTE: The `uv_build` backend always uses path-based `.pth` files.

### pip with setuptools
When using `pip` with setuptools-based projects, there are two ways to avoid import hooks:
[compat mode](https://setuptools.pypa.io/en/latest/userguide/development_mode.html#legacy-behavior)
and [strict mode](https://setuptools.pypa.io/en/latest/userguide/development_mode.html#strict-editable-installs).

### Hatch / Hatchling

[Hatchling](https://hatch.pypa.io/1.9/config/build/) uses path-based `.pth` files by default.
It will only use import hooks if you set [`dev-mode-exact` to true](https://hatch.pypa.io/latest/config/build/#dev-mode).

### PDM

[PDM](https://backend.pdm-project.org/) uses path-based `.pth` files by default.
It will only use import hooks if you set
[`editable-backend` to "editables"](https://backend.pdm-project.org/build_config/#choose-the-editable-build-format).

### Poetry / Poetry-core
[Poetry-core](https://github.com/python-poetry/poetry-core) backend always uses path-based `.pth` files.

## Debugging Import Issues

Pyrefly has a `dump-config` command that dumps the import-related config options it is using for
each file it is checking. To use it, simply replace `check` with `dump-config` in your
command-line invocation.
